<?php
/**
 * Provides cryptographically secure random bytes generation
 *
 * Warning: Use SecureRandom instead of directly using this class!
 *
 * @copyright  Based on frostschutz's post at
 *             http://forums.thedailywtf.com/forums/t/16453.aspx
 * @copyright  Copyright (c) 2011 The Fajr authors (see AUTHORS).
 *             Use of this source code is governed by a MIT license that can be
 *             found in the LICENSE file in the project root directory.
 * @package    Fajr
 * @subpackage Security
 * @author     frostschutz
 * @author     Peter Perešíni <ppershing+fajr@gmail.com>
 * @filesource
 */
namespace fajr\security;

use libfajr\base\Preconditions;

/**
 * Generates random bytes using linux random device.
 *
 * Warning: Use SecureRandom instead of directly using this class!
 *
 * @package    Fajr
 * @subpackage Security
 * @author     Peter Perešíni <ppershing+fajr@gmail.com>
 */
class SecureRandomLinuxProvider implements SecureRandomProvider {
  /**
   * File with random bytes.
   *
   * Note: we don't use /dev/random as it may block for a big time.
   * This seems to be not the problem with the security.
   * According to http://lwn.net/Articles/261091/ the urandom security
   * should be quite sufficient.
   */
  const RANDOM_FILE = '/dev/urandom';

  /**
   * Check whether we can generate bytes using linux random.
   *
   * @returns bool true if linux random is available
   */
  public static function isAvailable()
  {
    return @is_readable(self::RANDOM_FILE);
  }

  /**
   * Returns a securely generated random bytes from linux random.
   *
   * Warning: use SecureRandom::random() instead
   *
   * @param int $count number of random bytes to be generated
   *
   * @returns bytes|false generated bytes or false on error
   */
  public function randomBytes($count) {
    Preconditions::check(is_int($count));
    Preconditions::check($count > 0);

    if (!self::isAvailable()) {
      return false;
    }

    $handle = @fopen(self::RANDOM_FILE, 'rb');
    $output = @fread($handle, $count);
    @fclose($handle);
    if (strlen($output) !== $count) {
      return false; // oops, some problem reading required number of bytes
    }
    return $output;
  }
}
